package com.tsfyun.scm.service.impl.customer;

import cn.hutool.core.collection.CollUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.tsfyun.common.base.dto.FileQTO;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.domain.AgreementStatusEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.ExpAgreementStatusEnum;
import com.tsfyun.common.base.enums.exp.ExpBusinessTypeEnum;
import com.tsfyun.common.base.enums.exp.ExpSettlementModeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.DateUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.scm.dto.customer.ExpAgreementDTO;
import com.tsfyun.scm.dto.customer.ExpAgreementPlusDTO;
import com.tsfyun.scm.dto.customer.ExpAgreementQTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.customer.ExpAgreement;
import com.tsfyun.scm.mapper.customer.ExpAgreementMapper;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.customer.IExpAgreementService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.file.IUploadFileService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.IStatusHistoryService;
import com.tsfyun.scm.service.system.ISubjectService;
import com.tsfyun.scm.util.TsfWeekendSqls;
import com.tsfyun.scm.vo.customer.ExpAgreementVO;
import com.tsfyun.scm.vo.system.SubjectVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 出口协议 服务实现类
 * </p>
 *
 *
 * @since 2021-09-07
 */
@Service
public class ExpAgreementServiceImpl extends ServiceImpl<ExpAgreement> implements IExpAgreementService {

    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private IUploadFileService uploadFileService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private ExpAgreementMapper expAgreementMapper;
    @Autowired
    private ICommonService commonService;

    @Override
    public PageInfo<ExpAgreementVO> pageList(ExpAgreementQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto,Map.class);
        List<ExpAgreementVO> list = expAgreementMapper.list(params);
        return new PageInfo<>(list);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long add(ExpAgreementPlusDTO dto) {
        ExpAgreementDTO expAgreementDTO = dto.getAgreement();
        //失效日期>生效日期
        TsfPreconditions.checkArgument(expAgreementDTO.getInvalidDate().after(expAgreementDTO.getEffectDate()),new ServiceException("失效日期必须大于生效日期"));
        //失效日期>当前时间
        TsfPreconditions.checkArgument(expAgreementDTO.getInvalidDate().after(new Date()),new ServiceException("失效日期必须大于当前日期"));
        //获取甲方(客户)
        Customer customer = customerService.findByNameWithRight(expAgreementDTO.getPartyaName());
        // 根据客户查询是否有存在未完成的报价
        if(expAgreementMapper.countUndone(customer.getId())>0){
            throw new ServiceException("当前客户已经存在未完成的出口报价，不允许新建");
        }

        ExpAgreement expAgreement = beanMapper.map(expAgreementDTO,ExpAgreement.class);
        expAgreement.setCustomerId(customer.getId());
        expAgreement.setPartyaName(customer.getName());
        expAgreement.setPartyaSocialNo(customer.getSocialNo());
        expAgreement.setPartyaLegalPerson(customer.getLegalPerson());
        expAgreement.setPartyaTel(customer.getTel());
        expAgreement.setPartyaFax(customer.getFax());
        expAgreement.setPartyaAddress(customer.getAddress());

        //获取乙方(主体公司)
        SubjectVO subjectVO = subjectService.findByCode();
        TsfPreconditions.checkArgument(Objects.nonNull(subjectVO),new ServiceException("获取乙方信息失败"));
        expAgreement.setPartybName(subjectVO.getName());
        expAgreement.setPartybSocialNo(subjectVO.getSocialNo());
        expAgreement.setPartybLegalPerson(subjectVO.getLegalPerson());
        expAgreement.setPartybTel(subjectVO.getTel());
        expAgreement.setPartybFax(subjectVO.getFax());
        expAgreement.setPartybAddress(subjectVO.getAddress());
        expAgreement.setIsSignBack(Boolean.FALSE);
        expAgreement.setBusinessType(ExpBusinessTypeEnum.EXP_COOPERATE.getCode());//默认自营出口
        expAgreement.setIsTaxAdvance(expAgreementDTO.getIsTaxAdvance());
        expAgreement.setSettlementMode(expAgreementDTO.getSettlementMode());
        // 实际失效日期
        expAgreement.setActualInvalidDate(DateUtils.addYear(expAgreement.getInvalidDate(),expAgreement.getDelayYear()));
        //报价描述
        ExpSettlementModeEnum expSettlementModeEnum = ExpSettlementModeEnum.of(expAgreement.getSettlementMode());
        String quoteDescribe = Objects.equals(expAgreement.getIsTaxAdvance(),Boolean.TRUE) ? "垫税" : "不垫税";
        switch (expSettlementModeEnum){
            case DECLARE_TOTAL_PERCENT:
                quoteDescribe += " 按报关总价"+expAgreement.getAgencyFee()+"%收取";
                break;
            case ONE_DOLLAR:
                quoteDescribe += " 每1美金收取人民币"+expAgreement.getAgencyFee()+"元";
                break;
        }
        quoteDescribe += ",最低收费："+expAgreement.getMinAgencyFee();
        expAgreement.setQuoteDescribe(quoteDescribe);

        //状态
        ExpAgreementStatusEnum statusEnum = Objects.equals(Boolean.TRUE,expAgreementDTO.getSubmitAudit())?ExpAgreementStatusEnum.WAIT_EXAMINE:ExpAgreementStatusEnum.SAVED;
        expAgreement.setStatusId(statusEnum.getCode());

        try{
            super.saveNonNull(expAgreement);
            //记录历史状态
            statusHistoryService.saveHistory(DomainOprationEnum.EXP_AREEMENT_ADD,
                    expAgreement.getId().toString(), ExpAgreement.class.getName(),
                    statusEnum.getCode(),statusEnum.getName(),
                    Objects.equals(ExpAgreementStatusEnum.SAVED,statusEnum)?"临时保存":"提交审核"
            );
        }catch (DuplicateKeyException e) {
            if(e.getMessage().contains("UK_exp_agreement_doc_no")) {
                throw new ServiceException("协议编号已经存在请修改");
            }else {
                throw new ServiceException("保存失败，请检查数据是否填写正确");
            }
        }
        //关联文件信息
        uploadFileService.relateFile(expAgreement.getId().toString(),dto.getFile());
        //生成任务通知
        createNotice(expAgreement,customer);
        return expAgreement.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(ExpAgreementPlusDTO dto) {
        ExpAgreementDTO expAgreementDTO = dto.getAgreement();
        //失效日期>生效日期
        TsfPreconditions.checkArgument(expAgreementDTO.getInvalidDate().after(expAgreementDTO.getEffectDate()),new ServiceException("失效日期必须大于生效日期"));
        //失效日期>当前时间
        TsfPreconditions.checkArgument(expAgreementDTO.getInvalidDate().after(new Date()),new ServiceException("失效日期必须大于当前日期"));
        ExpAgreement expAgreement = super.getById(expAgreementDTO.getId());
        Optional.ofNullable(expAgreement).orElseThrow(()->new ServiceException("出口协议不存在，请刷新页面重试"));
        //原始单据状态
        ExpAgreementStatusEnum historyStatus = ExpAgreementStatusEnum.of(expAgreement.getStatusId());
        //验证状态是否可以执行操作
        DomainOprationEnum oprationEnum = DomainOprationEnum.EXP_AGREEMENT_EDIT;
        DomainStatus.getInstance().check(oprationEnum, expAgreement.getStatusId());

        //取原来客户信息更新，修改不能更改客户
        Customer customer = customerService.getById(expAgreement.getCustomerId());
        expAgreement.setCustomerId(customer.getId());
        expAgreement.setPartyaName(customer.getName());
        expAgreement.setPartyaSocialNo(customer.getSocialNo());
        expAgreement.setPartyaLegalPerson(customer.getLegalPerson());
        expAgreement.setPartyaTel(customer.getTel());
        expAgreement.setPartyaFax(customer.getFax());
        expAgreement.setPartyaAddress(customer.getAddress());

        //获取乙方(主体公司)
        SubjectVO subjectVO = subjectService.findByCode();
        TsfPreconditions.checkArgument(Objects.nonNull(subjectVO),new ServiceException("获取乙方信息失败"));
        expAgreement.setPartybName(subjectVO.getName());
        expAgreement.setPartybSocialNo(subjectVO.getSocialNo());
        expAgreement.setPartybLegalPerson(subjectVO.getLegalPerson());
        expAgreement.setPartybTel(subjectVO.getTel());
        expAgreement.setPartybFax(subjectVO.getFax());
        expAgreement.setPartybAddress(subjectVO.getAddress());
        expAgreement.setBusinessType(ExpBusinessTypeEnum.EXP_COOPERATE.getCode());//默认自营出口
        expAgreement.setDeclareType(expAgreementDTO.getDeclareType());

        expAgreement.setSigningDate(expAgreementDTO.getSigningDate());
        expAgreement.setEffectDate(expAgreementDTO.getEffectDate());
        expAgreement.setInvalidDate(expAgreementDTO.getInvalidDate());
        expAgreement.setDelayYear(expAgreementDTO.getDelayYear());
        // 实际失效日期
        expAgreement.setActualInvalidDate(DateUtils.addYear(expAgreement.getInvalidDate(),expAgreement.getDelayYear()));
        expAgreement.setIsTaxAdvance(expAgreementDTO.getIsTaxAdvance());
        expAgreement.setSettlementMode(expAgreementDTO.getSettlementMode());
        expAgreement.setAgencyFee(expAgreementDTO.getAgencyFee());
        expAgreement.setMinAgencyFee(expAgreementDTO.getMinAgencyFee());
        expAgreement.setMemo(expAgreementDTO.getMemo());

        //报价描述
        ExpSettlementModeEnum expSettlementModeEnum = ExpSettlementModeEnum.of(expAgreement.getSettlementMode());
        String quoteDescribe = Objects.equals(expAgreement.getIsTaxAdvance(),Boolean.TRUE) ? "垫税" : "不垫税";
        switch (expSettlementModeEnum){
            case DECLARE_TOTAL_PERCENT:
                quoteDescribe += " 按报关总价"+expAgreement.getAgencyFee()+"%收取";
                break;
            case ONE_DOLLAR:
                quoteDescribe += " 每1美金收取人民币"+expAgreement.getAgencyFee()+"元";
                break;
        }
        quoteDescribe += ",最低收费："+expAgreement.getMinAgencyFee();
        expAgreement.setQuoteDescribe(quoteDescribe);

        //状态
        ExpAgreementStatusEnum statusEnum = Objects.equals(Boolean.TRUE,expAgreementDTO.getSubmitAudit())?ExpAgreementStatusEnum.WAIT_EXAMINE:ExpAgreementStatusEnum.SAVED;
        expAgreement.setStatusId(statusEnum.getCode());

        try{
            super.updateById(expAgreement);
            //记录历史状态
            statusHistoryService.saveHistory(oprationEnum,
                    expAgreement.getId().toString(), ExpAgreement.class.getName(),
                    historyStatus.getCode(),historyStatus.getName(),
                    statusEnum.getCode(),statusEnum.getName(),
                    Objects.equals(ExpAgreementStatusEnum.SAVED,statusEnum)?"临时保存":"提交审核"
            );
        }catch (DuplicateKeyException e) {
            if(e.getMessage().contains("UK_exp_agreement_doc_no")) {
                throw new ServiceException("协议编号已经存在请修改");
            }else {
                throw new ServiceException("保存失败，请检查数据是否填写正确");
            }
        }
        //生成任务通知
        createNotice(expAgreement,customer);
    }

    @Override
    public ExpAgreementVO detail(Long id, String operation) {
        ExpAgreement expAgreement = super.getById(id);
        Optional.ofNullable(expAgreement).orElseThrow(()->new ServiceException("出口协议不存在，请刷新页面重试"));
        //验证状态是否可以执行操作
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation), expAgreement.getStatusId());
        return beanMapper.map(expAgreement,ExpAgreementVO.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void examine(TaskDTO dto) {
        ExpAgreement agreement = commonService.changeDocumentStatus(dto);
        ExpAgreementStatusEnum status = ExpAgreementStatusEnum.of(agreement.getStatusId());
        if(Objects.equals(status,ExpAgreementStatusEnum.AUDITED)){
            //将其它生效协议置为 作废
            TsfWeekendSqls wheres = TsfWeekendSqls.<ExpAgreement>custom()
                    .andEqualTo(false,ExpAgreement::getCustomerId,agreement.getCustomerId())//客户
                    .andEqualTo(false,ExpAgreement::getStatusId, AgreementStatusEnum.AUDITED.getCode())//已审核
                    .andNotEqualTo(false,ExpAgreement::getId,agreement.getId());//排除自己
            List<ExpAgreement> agreementList = expAgreementMapper.selectByExample(Example.builder(ExpAgreement.class).where(wheres).build());
            AgreementStatusEnum nowStatusEnum = AgreementStatusEnum.INVALID;
            DomainOprationEnum oprationEnum = DomainOprationEnum.AGREEMENT_SIGN;
            agreementList.stream().forEach(arg -> {
                //原始单据状态
                AgreementStatusEnum historyStatus = AgreementStatusEnum.of(arg.getStatusId());
                arg.setStatusId(nowStatusEnum.getCode());
                super.updateById(arg);
                //记录历史状态
                statusHistoryService.saveHistory(oprationEnum,
                        arg.getId().toString(),ExpAgreement.class.getName(),
                        historyStatus.getCode(),historyStatus.getName(),
                        nowStatusEnum.getCode(),nowStatusEnum.getName(),
                        String.format("新协议[%s]生效，作废此协议",agreement.getDocNo()));
            });
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long id) {
        ExpAgreement expAgreement = super.getById(id);
        Optional.ofNullable(expAgreement).orElseThrow(()->new ServiceException("出口协议不存在，请刷新页面重试"));
        DomainStatus.getInstance().check(DomainOprationEnum.EXP_AGREEMENT_DELETE,expAgreement.getStatusId());
        //删除协议
        super.removeById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void signBack(List<Long> ids) {
        List<ExpAgreement> expAgreements = expAgreementMapper.selectByIdsAndIsSignBack(ids,Boolean.FALSE);
        expAgreements.stream().forEach(expAgreement -> {
            //检查是否存在原件附件
            FileQTO qto = new FileQTO();
            qto.setDocId(expAgreement.getId().toString());
            qto.setDocType("exp_agreement");
            qto.setBusinessType("agr_original");
            Integer count = uploadFileService.count(qto);
            TsfPreconditions.checkArgument(count > 0,new ServiceException(String.format("出口协议【%s】原件附件不存在",expAgreement.getDocNo())));
            expAgreement.setIsSignBack(Boolean.TRUE);
            expAgreement.setSignBackPerson(SecurityUtil.getCurrentPersonName());
            super.updateById(expAgreement);
        });
    }

    @Override
    public List<ExpAgreementVO> checkCustomerExpAgreement(Long customerId) {
        TsfWeekendSqls wheres = TsfWeekendSqls.<ExpAgreement>custom()
                .andEqualTo(false,ExpAgreement::getCustomerId,customerId)//客户
                .andEqualTo(false,ExpAgreement::getStatusId,ExpAgreementStatusEnum.AUDITED.getCode())//已审核
                .andGreaterThan(false,ExpAgreement::getActualInvalidDate, LocalDateTime.now().plusDays(-1));//时间有效期(当前时间带时分秒减掉一天)
        List<ExpAgreement> agreementList = expAgreementMapper.selectByExample(Example.builder(ExpAgreement.class).where(wheres).build());
        if(CollUtil.isEmpty(agreementList)){
            throw new ServiceException("未找到当前客户有效的出口协议，请确认是否存在已确认协议");
        }
        return beanMapper.mapAsList(agreementList,ExpAgreementVO.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void scheduleInvalidExpAgreement() {
        expAgreementMapper.invalidExpAgreement();
    }

    @Override
    public ExpAgreement validationAgreement(Long id) {
        TsfWeekendSqls wheres = TsfWeekendSqls.<ExpAgreement>custom()
                .andEqualTo(false,ExpAgreement::getId,id)//ID
                .andEqualTo(false,ExpAgreement::getStatusId,AgreementStatusEnum.AUDITED.getCode())//已审核
                .andGreaterThan(false,ExpAgreement::getActualInvalidDate,LocalDateTime.now().plusDays(-1));//时间有效期(当前时间带时分秒减掉一天)
        return expAgreementMapper.selectOneByExample(Example.builder(ExpAgreement.class).where(wheres).build());
    }

    public void createNotice(ExpAgreement expAgreement,Customer customer) {
        //待审核
        if(Objects.equals(ExpAgreementStatusEnum.WAIT_EXAMINE.getCode(),expAgreement.getStatusId())) {
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.EXPAGREEMENT.getCode());
            taskNoticeContentDTO.setDocumentId(expAgreement.getId().toString());
            taskNoticeContentDTO.setCustomerId(customer.getId());
            taskNoticeContentDTO.setCustomerName(customer.getName());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.EXP_AGREEMENT_EXAMINE.getCode());
            taskNoticeContentDTO.setContent(String.format("【%s】提交了一份【%s】出口协议报价单需要您审批。", SecurityUtil.getCurrentPersonName(),customer.getName()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",expAgreement.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }
    }

}
